// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: xguo

#include "coin2/strategy/order_executor/working_order_state.h"

namespace coin2::strategy::order_executor {

namespace {
using OrderSide = coin::proto::OrderSide;

bool IsBuy(OrderSide order_side) {
  return (
      order_side == OrderSide::BUY_ORDER || order_side == OrderSide::BUY_OPEN_ORDER ||
      order_side == OrderSide::BUY_CLOSE_ORDER);
}

bool IsSell(OrderSide order_side) {
  return (
      order_side == OrderSide::SELL_ORDER || order_side == OrderSide::SELL_OPEN_ORDER ||
      order_side == OrderSide::SELL_CLOSE_ORDER);
}

bool AcceptAllPolicy(const WorkingOrderState::OrderContext*, WorkingOrderState::AcceptPolicyType) {
  // accept all policy
  return true;
}

}  // namespace

WorkingOrderState::WorkingOrderState(
    const std::string native_symbol,
    IOrderSubsystem* os,
    AcceptPolicy ap)
    : native_product_(native_symbol), os_(os) {
  accept_policy_ = ap;
}

WorkingOrderState::WorkingOrderState(const std::string native_symbol, IOrderSubsystem* os)
    : native_product_(native_symbol), os_(os) {
  accept_policy_ = &AcceptAllPolicy;
}

void WorkingOrderState::ResetOrderState() {
  sell_working_or_on_the_fly_orders_.clear();
  sell_on_the_fly_orders_.clear();
  sell_working_orders_.clear();
  sell_cancel_on_the_fly_orders_.clear();
  sell_orders_.clear();
  sell_on_the_fly_potential_ = 0;
  sell_working_potential_ = 0;
  sell_cancel_on_the_fly_potential_ = 0;
  sell_potential_ = 0;

  buy_working_or_on_the_fly_orders_.clear();
  buy_on_the_fly_orders_.clear();
  buy_working_orders_.clear();
  buy_cancel_on_the_fly_orders_.clear();
  buy_orders_.clear();
  buy_on_the_fly_potential_ = 0;
  buy_working_potential_ = 0;
  buy_cancel_on_the_fly_potential_ = 0;
  buy_potential_ = 0;
}

void WorkingOrderState::UpdateOrderState() {
  ResetOrderState();
  for (auto* order : os_->gateway()->order_manager().GetAllWorkingOrders()) {
    if (order->native_product() != native_product_) {
      continue;
    }
    if (order->order_spec().use_amend && !order->is_accepted()) {
      continue;
    }
    const auto order_side = order->order_spec().order_side;
    const auto order_qty = order->order_qty() - order->filled_qty();

    if (IsSell(order_side)) {
      if (order->is_cancel_pending()) {
        if (accept_policy_(order, AcceptPolicyType::SELL_CANCEL_ON_THE_FLY_ORDERS)) {
          sell_cancel_on_the_fly_orders_.push_back(order);
        }
        sell_cancel_on_the_fly_potential_ -= order_qty;
      } else if (order->is_accepted()) {
        if (accept_policy_(order, AcceptPolicyType::SELL_WORKING_ORDERS)) {
          sell_working_orders_.push_back(order);
          sell_working_or_on_the_fly_orders_.push_back(order);
        }
        sell_working_potential_ -= order_qty;
      } else {
        if (accept_policy_(order, AcceptPolicyType::SELL_ON_THE_FLY_ORDERS)) {
          sell_on_the_fly_orders_.push_back(order);
          sell_working_or_on_the_fly_orders_.push_back(order);
        }
        sell_on_the_fly_potential_ -= order_qty;
      }
      if (accept_policy_(order, AcceptPolicyType::SELL_ORDERS)) {
        sell_orders_.push_back(order);
      }
      sell_potential_ -= order_qty;
    } else if (IsBuy(order_side)) {
      if (order->is_cancel_pending()) {
        if (accept_policy_(order, AcceptPolicyType::BUY_CANCEL_ON_THE_FLY_ORDERS)) {
          buy_cancel_on_the_fly_orders_.push_back(order);
        }
        buy_cancel_on_the_fly_potential_ += order_qty;
      } else if (order->is_accepted()) {
        if (accept_policy_(order, AcceptPolicyType::BUY_WORKING_ORDERS)) {
          buy_working_orders_.push_back(order);
          buy_working_or_on_the_fly_orders_.push_back(order);
        }
        buy_working_potential_ += order_qty;
      } else {
        if (accept_policy_(order, AcceptPolicyType::BUY_ON_THE_FLY_ORDERS)) {
          buy_on_the_fly_orders_.push_back(order);
          buy_working_or_on_the_fly_orders_.push_back(order);
        }
        buy_on_the_fly_potential_ += order_qty;
      }
      if (accept_policy_(order, AcceptPolicyType::BUY_ORDERS)) {
        buy_orders_.push_back(order);
      }
      buy_potential_ += order_qty;
    } else {
      NOTREACHED() << "Unknown order side: " << static_cast<int>(order_side);
    }
  }
}

}  // namespace coin2::strategy::order_executor
